﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace Sci
{
    /// <summary>
    /// Sci.UpdateTool.AutoUpdate();            // 应用自动检测更新；
    /// 
    /// 更新原理：
    /// 根据ToolUrl_MD5获取当前应用线上的md5信息，
    /// 通过Update.exe判断当前应用的MD5值与线上的md5信息是否相同，
    /// 若不同，则结束当前进程，从ToolUrl_MD5中解析当前应用网络路径，下载并启动。Update.exe自删除。
    /// </summary>
    class UpdateTool
    {
        // 当前命名空间名称
        //public static string NAMESPACE = GetNamespace(Assembly.GetEntryAssembly());
        public static string NAMESPACE = "AliUserId";

        /// <summary>
        /// 获取Assembly所在的命名空间名称
        /// </summary>
        private static string GetNamespace(Assembly asssembly)
        {
            string Namespace = "";
            Type[] types = asssembly.GetTypes();
            if (types != null && types.Length > 0)
            {
                Namespace = types[0].Namespace;
            }
            return Namespace;
        }

        /// <summary>
        /// 获取当前运行路径
        /// </summary>
        public static string curDir()
        {
            return AppDomain.CurrentDomain.BaseDirectory;
        }

        /// <summary>
        /// 检测目录是否存在，若不存在则创建
        /// </summary>
        public static void checkDir(string path)
        {
            if (!Directory.Exists(path))
            {
                Directory.CreateDirectory(path);
            }
        }

        /// <summary>
        /// 保存Byte数组为文件
        /// </summary>
        public static void SaveFile(Byte[] array, string path, bool repalce = false)
        {
            if (repalce && System.IO.File.Exists(path)) System.IO.File.Delete(path);    // 若目标文件存在，则替换
            if (!System.IO.File.Exists(path))
            {
                // 创建输出流
                System.IO.FileStream fs = new System.IO.FileStream(path, System.IO.FileMode.Create);

                //将byte数组写入文件中
                fs.Write(array, 0, array.Length);
                fs.Close();
            }
        }

        /// <summary>
        /// 为arg添加引号
        /// </summary>
        private static string AddQuotation(string arg)
        {
            if (arg.EndsWith("\\") && !arg.EndsWith("\\\\")) arg += "\\";
            arg = "\"" + arg + "\"";

            return arg;
        }

        /// <summary>
        /// 获取Resource资源
        /// </summary>
        private static byte[] getResource(string name)
        {
            string Namespace = Assembly.GetEntryAssembly().GetTypes()[0].Namespace;
            System.Resources.ResourceManager rm = new System.Resources.ResourceManager(Namespace + ".Properties.Resources", System.Reflection.Assembly.GetExecutingAssembly());
            byte[] bytes = (byte[])rm.GetObject(name);

            return bytes;
        }

        // "%~dp0Update.exe" "[CONFIG]https://git.oschina.net/joymeng/channelDemo/raw/master/MD5.txt" "E:\tmp2\Update_Files\\" "渠道计费包\0000001\\"
        /// <summary>
        /// 调用Update.exe，更新以perfix为前缀的配置文件
        /// </summary>
        public static void updateFiles(string url, string perfix, bool RESTART = false)
        {
            string update_EXE = curDir() + "Update.exe";

            //byte[] bytes = getResource("Update");
            byte[] bytes = GetByte();

            if (!File.Exists(update_EXE)) SaveFile(bytes, update_EXE, false);
            else if (!updateIsRunning()) SaveFile(bytes, update_EXE, true);   // 更新Update.exe

            string path = curDir();
            url = AddQuotation("[CONFIG]" + url);
            path = AddQuotation(path);
            perfix = AddQuotation(perfix);
            update_EXE = AddQuotation(update_EXE);

            // 调用更新插件执行软件更新逻辑
            String arg = url + " " + path + " " + perfix;
            if (RESTART) arg += " " + "RESTART";
            System.Diagnostics.Process.Start(update_EXE, arg);

            autoDeletUpdateExe();   // 更新后删除update.exe
        }

        private static Timer timer = null;
        private static void autoDeletUpdateExe(object sender = null, EventArgs e = null)
        {
            string update_EXE = curDir() + "Update.exe";
            if (!File.Exists(update_EXE)) return;

            if (!updateIsRunning())
            {
                File.Delete(update_EXE);

                if (timer != null)
                {
                    timer.Stop();
                    timer = null;
                }
            }
            else
            {
                if (timer == null)
                {
                    timer = new Timer();
                    timer.Interval = 700;
                    timer.Tick += new System.EventHandler(autoDeletUpdateExe);
                    timer.Start();
                }
            }
        }

        /// <summary>
        /// 应用自更新,也可更新MD5.txt中配置的任意文件
        /// </summary>
        /// <param name="ToolUrl_MD5">工具MD5信息文件的url地址</param>
        /// <param name="perfix_EXE">当前待更新的应用名称</param>
        public static void AutoUpdate(string ToolUrl_MD5 = null, string perfix_EXE = null)
        {
            if (ToolUrl_MD5 == null) ToolUrl_MD5 = "";
            if (perfix_EXE == null) perfix_EXE = "";

            if (ToolUrl_MD5.Equals("")) ToolUrl_MD5 = "https://scimence.gitee.io/" + NAMESPACE + "/MD5.txt";
            if (perfix_EXE.Equals("")) perfix_EXE = NAMESPACE + ".exe";

            updateFiles(ToolUrl_MD5, perfix_EXE, true);                 // 更新当前应用
            updateFiles(ToolUrl_MD5, NAMESPACE + ".data", true);        // 更新当前应用对应data
        }

        /// <summary>
        /// 打开本地路径
        /// </summary>
        public static void openChannelDir(string channelDir)
        {
            string localDir = curDir() + channelDir.Replace("/", "\\");
            checkDir(localDir);

            if (Directory.Exists(localDir))
            {
                System.Diagnostics.Process.Start("explorer.exe", "/e, " + localDir);
            }
        }

        // 判断当前是有更新任务正在执行
        public static bool updateIsRunning()
        {
            // 获取Update.exe后台进程
            System.Diagnostics.Process[] processes1 = System.Diagnostics.Process.GetProcessesByName("Update");

            // 无更新进程Update.exe，表示更新完成
            return (processes1 != null && processes1.Length != 0);
        }


        #region 获取应用更新插件

        private static byte[] GetByte()
        {
            string data_run = getData("https://scimence.gitee.io/scitools/DATA/UpdateTool.data");
            byte[] bytes = ToBytes(data_run);
            return bytes;
        }

        /// <summary>  
        /// 解析字符串为Bytes数组
        /// </summary>  
        private static byte[] ToBytes(string data)
        {
            byte[] B = new byte[data.Length / 2];
            char[] C = data.ToCharArray();

            for (int i = 0; i < C.Length; i += 2)
            {
                byte b = ToByte(C[i], C[i + 1]);
                B[i / 2] = b;
            }

            return B;
        }

        /// <summary>  
        /// 每两个字母还原为一个字节  
        /// </summary>  
        private static byte ToByte(char a1, char a2)
        {
            return (byte)((a1 - 'a') * 16 + (a2 - 'a'));
        }

        /// <summary>
        /// 从指定dataUrl载入数据，并在本地缓存
        /// </summary>
        /// <param name="dataUrl"></param>
        /// <returns></returns>
        private static string getData(string dataUrl)
        {
            string data = "";
            try
            {
                string fileName = dataUrl.Substring(dataUrl.LastIndexOf("/") + 1);
                string localPath = AppDomain.CurrentDomain.BaseDirectory + fileName;

                // 优先从本地载入数据
                if (File.Exists(localPath))
                {
                    data = File.ReadAllText(localPath).Trim();
                    if (data.Trim().Equals("")) File.Delete(localPath);
                }

                // 若本地无数据，则从网址加载
                if (!File.Exists(localPath))
                {
                    System.Net.WebClient client = new System.Net.WebClient();
                    data = client.DownloadString(dataUrl).Trim();

                    File.WriteAllText(localPath, data);     // 本地缓存
                }

            }
            catch (Exception) { }
            return data;
        }

        #endregion

        
    }

}
